/*
 * server.cc
 *
 *  Created on: Jun 29, 2011
 *      Author: peckb1
 */

#include "system.h"
#include "network.h"
#include "post.h"
#include "interrupt.h"
#include "bool.h"
#include "syscall.h"

#include "serverLock.h"
#include "serverCondition.h"

#include "serverStringMV.h"
#include "serverIntArrayMV.h"

#include "messageToProcess.h"

#include <stdio.h>
#include <string.h>
#include <map>

std::map<int, ServerLock*> locksMap;
int currentLockId = 0;

std::map<int, ServerCondition*> conditionsMap;
int currentConditionId = 0;

std::map<int, ServerIntArrayMV*> intMVMap;
int currentIntMVId = 0;

List* messagesToHandle = new List;

int currentTime = 1;
int myMachineId = -1;
int myMailBox = 0;
int currentTimeOfOtherServers[NUMBER_OF_SERVERS];

/**********************************************************************************
 *                          SEND MESSAGE HELPER                                   *
 **********************************************************************************/
void InternalSend(char* message, char data[MaxMailSize], int machineId, int mailBox, PacketHeader outPktHdr, MailHeader outMailHdr) {
    // send out a response;
    outPktHdr.to  = machineId;   //machine ID of target
    outMailHdr.to = mailBox;     // mailbox of Target
    outMailHdr.length = strlen(data) + 1;

    // make sure things went well with the send
    bool success = postOffice->Send(outPktHdr, outMailHdr, data);
    if(!success) {
        printf("ERROR: Could not send %s to Machine #%i Box #%i.\n", message, machineId, mailBox);
    }
}

void ForwardMessageToServers(PacketHeader inPktHdr, MailHeader inMailHdr, PacketHeader outPktHdr, MailHeader outMailHdr, char* originalMailBuffer) {
    char data[MaxMailSize];
    sprintf(data, "%i,%i,%i,%i,%s", ServerForwardedMessage, currentTime, inPktHdr.from, inMailHdr.from, originalMailBuffer);
    printf("Fowarding message(%s) at time %i.\n", data, currentTime);
    currentTime ++;
    for(int i = 0; i < NUMBER_OF_SERVERS; i++) {
        InternalSend("Server Forward", data, i, 0, outPktHdr, outMailHdr);
    }
}

void SendHeartBeatMessage(int timestamp, PacketHeader outPktHdr, MailHeader outMailHdr) {
    char data[MaxMailSize];
    sprintf(data, "%i,%i", ServerHeartbeat, timestamp);
    for(int i = 0; i < NUMBER_OF_SERVERS; i++) {
        if(i != myMachineId) {
            InternalSend("Server Heartbeat", data, i, 0, outPktHdr, outMailHdr);
        }
    }
}

/**********************************************************************************
 *                              LOCK CALLS                                        *
 **********************************************************************************/

void InternalCreateLock(char* token, PacketHeader outPktHdr, PacketHeader inPktHdr, MailHeader outMailHdr, MailHeader inMailHdr, MessageToProcess* message) {
    char data[MaxMailSize];
    char* name = token;
    printf("Machine %i at box %i wants to Create Lock with the name '%s'.\n",message->GetClientMachineId(), message->GetClientMailBox(), name);

    int idOfNewLock = -1;

    for(int i = 0; i < currentLockId; i++) {
        ServerLock* lock = locksMap[i];

        if(strcmp(lock->GetName(), name) == 0) {
            // found a match
            idOfNewLock = i;
            i = currentLockId;
        }
    }

    if(idOfNewLock == -1) {
        // create the new lock
        ServerLock* lock = new ServerLock(name, message->GetClientMachineId());
        idOfNewLock = currentLockId;
        currentLockId ++;
        locksMap.insert(std::pair<int, ServerLock*>(idOfNewLock, lock));
    }

    if(myMachineId == message->GetOriginalMachineId() && myMailBox == message->GetOriginalMachineMailBox()) {
        printf("Processed Lock Create Request and now sending a response because I was the original destination.\n");
        sprintf(data, "%i,%i", TRUE, idOfNewLock);
        InternalSend("Create Lock Response", data, message->GetClientMachineId(), message->GetClientMailBox(), outPktHdr, outMailHdr);
    } else {
        printf("Processed Lock Create Request and NOT sending a response because I was NOT the original destination.\n");
    }

}

void InternalAcquireLock(char* token, PacketHeader outPktHdr, PacketHeader inPktHdr, MailHeader outMailHdr, MailHeader inMailHdr, MessageToProcess* message) {
    char data[MaxMailSize];
    int requestedLockId = atoi(token);
    printf("Machine %i at box %i wants to Acquire lock #%i.\n",message->GetClientMachineId(), message->GetClientMailBox(), requestedLockId);

    // validate a few things
    if(requestedLockId >= 0 && requestedLockId < currentLockId) {
        // was at one point a valid Lock
        ServerLock* lock = locksMap[requestedLockId];
        if(!lock->IsDeleted() && !lock->IsScheduledForDeletion()) {
            MailAddress* address = new MailAddress(message->GetClientMachineId(), message->GetClientMailBox(), message->GetOriginalMachineId(), message->GetOriginalMachineMailBox());
            int status = lock->Acquire(address);
            if(status == 1) {
                // all went well, and they have a lock, send the response
                if(myMachineId == message->GetOriginalMachineId() && myMailBox == message->GetOriginalMachineMailBox()) {
                    printf("Processed Acquire Lock Request and now sending a response because I was the original destination.\n");
                    sprintf(data, "%i,%i", TRUE, requestedLockId);
                    InternalSend("Acquire Lock Response", data, message->GetClientMachineId(), message->GetClientMailBox(), outPktHdr, outMailHdr);
                } else {
                    printf("Processed Acquire Lock Request and NOT sending a response because I was NOT the original destination.\n");
                }
            }
        } else {
            printf("Machine %i at box %i wants to Acquire lock #%i - but it is deleted/scheduled for deletion.\n",message->GetClientMachineId(), message->GetClientMailBox(), requestedLockId);
        }
    } else {
        printf("Machine %i at box %i wants to Acquire lock #%i - but it is an invalid Id.\n",message->GetClientMachineId(), message->GetClientMailBox(), requestedLockId);
    }
}

void InternalReleaseLock(char* token, PacketHeader outPktHdr, PacketHeader inPktHdr, MailHeader outMailHdr, MailHeader inMailHdr, MessageToProcess* message) {
    char data[MaxMailSize];
    int requestedLockId = atoi(token);
    printf("Machine %i at box %i wants to Release lock #%i.\n",message->GetClientMachineId(), message->GetClientMailBox(), requestedLockId);

    // validate a few things
    if(requestedLockId >= 0 && requestedLockId < currentLockId) {
        // was at one point a valid Lock
        ServerLock* lock = locksMap[requestedLockId];
        if(!lock->IsDeleted()) {
            MailAddress* address = new MailAddress(message->GetClientMachineId(), message->GetClientMailBox(), message->GetOriginalMachineId(), message->GetOriginalMachineMailBox());
            MailAddress* nextOwner = lock->Release(address);
            if(nextOwner != NULL) {
                printf("Relase was ok, next person is waiting.\n");
                if(myMachineId == nextOwner->OwnersMachineId() && myMailBox == nextOwner->OwnersMailBox()) {
                    printf("Processed Acquire Lock Request and now sending a response because I was the original destination.\n");
                    // going to send an additional request to the person who now own the lock
                    sprintf(data, "%i,%i", TRUE, requestedLockId);
                    InternalSend("Acquire Lock Response", data, nextOwner->MachineId(), nextOwner->MailboxNumber(), outPktHdr, outMailHdr);
                } else {
                    printf("Processed Acquire Lock Request and NOT sending a response because I was NOT the original destination.\n");
                }
            } else {
                printf("Relase was ok, no one waiting.\n");
            }

            if(myMachineId == message->GetOriginalMachineId() && myMailBox == message->GetOriginalMachineMailBox()) {
                printf("Processed Release Lock Request and now sending a response because I was the original destination.\n");
                // send message to person who released the lock
                sprintf(data, "%i,%i", TRUE, requestedLockId);
                InternalSend("Release Lock Response", data, message->GetClientMachineId(), message->GetClientMailBox(), outPktHdr, outMailHdr);
            } else {
                printf("Processed Release Create Request and NOT sending a response because I was NOT the original destination.\n");
            }
        } else {
            printf("Machine %i at box %i wants to Release lock #%i - but it is deleted.\n",message->GetClientMachineId(), message->GetClientMailBox(), requestedLockId);
        }
    } else {
        printf("Machine %i at box %i wants to Release lock #%i - but it is an invalid Id.\n",message->GetClientMachineId(), message->GetClientMailBox(), requestedLockId);
    }
}

void InternalDestroyLock(char* token, PacketHeader outPktHdr, PacketHeader inPktHdr, MailHeader outMailHdr, MailHeader inMailHdr, MessageToProcess* message) {
    char data[MaxMailSize];
    int requestedLockId = atoi(token);
    printf("Machine %i at box %i wants to Destroy Lock #%i.\n",message->GetClientMachineId(), message->GetClientMailBox(), requestedLockId);

    // validate a few things
    if(requestedLockId >= 0 && requestedLockId < currentLockId) {
        // was at one point a valid Lock
        ServerLock* lock = locksMap[requestedLockId];
        if(!lock->IsDeleted() && !lock->IsScheduledForDeletion()) {
            MailAddress* address = new MailAddress(message->GetClientMachineId(), message->GetClientMailBox(), message->GetOriginalMachineId(), message->GetOriginalMachineMailBox());
            lock->DestroyLock(address);

            if(myMachineId == message->GetOriginalMachineId() && myMailBox == message->GetOriginalMachineMailBox()) {
                printf("Processed Destroy Lock Request and now sending a response because I was the original destination.\n");
                sprintf(data, "%i,%i", TRUE, requestedLockId);
                InternalSend("Destroy Lock Response", data, message->GetClientMachineId(), message->GetClientMailBox(), outPktHdr, outMailHdr);
            } else {
                printf("Processed Destroy Lock Request and NOT sending a response because I was NOT the original destination.\n");
            }
        } else {
            printf("Machine %i at box %i wants to Destroy lock #%i - but it is deleted/scheduled for deletion.\n",message->GetClientMachineId(), message->GetClientMailBox(), requestedLockId);
        }
    } else {
        printf("Machine %i at box %i wants to Destroy lock #%i - but it is an invalid Id.\n",message->GetClientMachineId(), message->GetClientMailBox(), requestedLockId);
    }
}

/**********************************************************************************
 *                             END LOCK CALLS                                     *
 **********************************************************************************/
/**********************************************************************************/
/**********************************************************************************
 *                                CV CALLS                                        *
 **********************************************************************************/

void InternalCreateCV(char* token, PacketHeader outPktHdr, PacketHeader inPktHdr, MailHeader outMailHdr, MailHeader inMailHdr, MessageToProcess* message) {
    char data[MaxMailSize];
    char* name = token;
    printf("Machine %i at box %i wants to Create CV with the name '%s'.\n",message->GetClientMachineId(), message->GetClientMailBox(), name);

    int idOfNewCV = -1;
    for(int i = 0; i < currentConditionId; i++) {
        ServerCondition* condition = conditionsMap[i];

        if(strcmp(condition->GetName(), name) == 0) {
            // found a match
            idOfNewCV = i;
            i = currentConditionId;
        }
    }

    if(idOfNewCV == -1) {
        // create the condition
        ServerCondition* condition = new ServerCondition(name, message->GetClientMachineId());
        idOfNewCV = currentConditionId;
        currentConditionId ++;
        conditionsMap.insert(std::pair<int, ServerCondition*>(idOfNewCV, condition));
    }

    if(myMachineId == message->GetOriginalMachineId() && myMailBox == message->GetOriginalMachineMailBox()) {
        printf("Processed Create CV Request and now sending a response because I was the original destination.\n");
        // send out a response;
        sprintf(data, "%i,%i", TRUE, idOfNewCV);
        InternalSend("Create CV Response", data, message->GetClientMachineId(), message->GetClientMailBox(), outPktHdr, outMailHdr);
    } else {
        printf("Processed Create CV Request and NOT sending a response because I was NOT the original destination.\n");
    }
}

void InternalWaitCV(char* token, PacketHeader outPktHdr, PacketHeader inPktHdr, MailHeader outMailHdr, MailHeader inMailHdr, MessageToProcess* message) {
    char data[MaxMailSize];
    int requestedCVId = atoi(token);
    int requestedLockId = -1;
    // get the LockId from the message sent
    token = strtok(NULL, ",");
    if(token) {
        requestedLockId = atoi(token);
    }
    if(requestedCVId != -1 && requestedLockId != -1) {
        printf("Machine %i at box %i wants to Wait on CV %i with Lock %i.\n",message->GetClientMachineId(), message->GetClientMailBox(), requestedCVId, requestedLockId);

        if(requestedCVId >=0 && requestedCVId < currentConditionId &&
           requestedLockId >=0 && requestedLockId < currentLockId) {

            ServerLock* lock = locksMap[requestedLockId];
            ServerCondition* condition = conditionsMap[requestedCVId];

            if(!lock->IsScheduledForDeletion() && !lock->IsDeleted() &&
               !condition->IsScheduledForDeletion() && !condition->IsDeleted()) {

                MailAddress* address = new MailAddress(message->GetClientMachineId(), message->GetClientMailBox(), message->GetOriginalMachineId(), message->GetOriginalMachineMailBox());
                MailAddress* personWaiting = condition->Wait(address, lock);
                if(personWaiting) {
                    if(myMachineId == personWaiting->OwnersMachineId() && myMailBox == personWaiting->OwnersMailBox()) {
                        printf("Processed Acquire Lock Request and now sending a response because I was the original destination.\n");
                        // signal the person who was waiting
                        sprintf(data, "%i,%i,", TRUE, requestedLockId);
                        InternalSend("Acquire Lock Response", data, personWaiting->MachineId(), personWaiting->MailboxNumber(), outPktHdr, outMailHdr);
                    } else {
                        printf("Processed Acquire Lock Request and NOT sending a response because I (%i,%i) was NOT the original destination (%i,%i).\n", myMachineId, myMailBox, personWaiting->OwnersMachineId(), personWaiting->OwnersMailBox());
                    }
                }
            } else {
                printf("Machine %i at box %i wants to Wait on a Lock/CV - but part has been scheduled for deletion/is deleted.\n",message->GetClientMachineId(), message->GetClientMailBox());
            }
        } else {
            printf("Machine %i at box %i wants to Wait on a Lock/CV - but an invalid Id was given.\n",message->GetClientMachineId(), message->GetClientMailBox());
        }
    }
}

void InternalSignalCV(char* token, PacketHeader outPktHdr, PacketHeader inPktHdr, MailHeader outMailHdr, MailHeader inMailHdr, MessageToProcess* message) {
    char data[MaxMailSize];
    int requestedCVId = atoi(token);
    int requestedLockId = -1;

    // get the LockId from the message sent
    token = strtok(NULL, ",");
    if(token) {
        requestedLockId = atoi(token);
    }
    if(requestedCVId != -1 && requestedLockId != -1) {
        printf("Machine %i at box %i wants to Signal CV %i with Lock %i.\n",message->GetClientMachineId(), message->GetClientMailBox(), requestedCVId, requestedLockId);

        if(requestedCVId >=0 && requestedCVId < currentConditionId &&
           requestedLockId >=0 && requestedLockId < currentLockId) {

            ServerLock* lock = locksMap[requestedLockId];
            ServerCondition* condition = conditionsMap[requestedCVId];

            if(!lock->IsDeleted() && !condition->IsDeleted()) {

                MailAddress* address = new MailAddress(message->GetClientMachineId(), message->GetClientMailBox(), message->GetOriginalMachineId(), message->GetOriginalMachineMailBox());
                MailAddress* addressWoken = condition->Signal(address, lock);

                if(addressWoken != NULL) {
                    if(myMachineId == addressWoken->OwnersMachineId() && myMailBox == addressWoken->OwnersMailBox()) {
                        printf("Processed Wait CV Request and now sending a response because I was the original destination.\n");
                        // signal the person who was waiting
                        sprintf(data, "%i,%i,", TRUE, requestedLockId);
                        InternalSend("Wait CV Response", data, addressWoken->MachineId(), addressWoken->MailboxNumber(), outPktHdr, outMailHdr);
                    } else {
                        printf("Processed Wait CV Request and NOT sending a response because I was NOT the original destination.\n");
                    }
                }

                if(myMachineId == message->GetOriginalMachineId() && myMailBox == message->GetOriginalMachineMailBox()) {
                    printf("Processed Signal CV Request and now sending a response because I was the original destination.\n");
                    // signal the person who called signal
                    sprintf(data, "%i,%i,%i", TRUE, requestedCVId, requestedLockId);
                    InternalSend("Signal CV Response", data, message->GetClientMachineId(), message->GetClientMailBox(), outPktHdr, outMailHdr);
                } else {
                    printf("Processed Signal CV Request and NOT sending a response because I was NOT the original destination.\n");
                }

            } else {
                printf("Machine %i at box %i wants to Signal on a Lock/CV - but part has been scheduled for deletion/is deleted.\n",message->GetClientMachineId(), message->GetClientMailBox());
            }
        } else {
            printf("Machine %i at box %i wants to Signal on a Lock/CV - but an invalid Id was given.\n",message->GetClientMachineId(), message->GetClientMailBox());
        }
    }
}

void InternalBroadcastCV(char* token, PacketHeader outPktHdr, PacketHeader inPktHdr, MailHeader outMailHdr, MailHeader inMailHdr, MessageToProcess* message) {
    char data[MaxMailSize];
    int requestedCVId = atoi(token);
    int requestedLockId = -1;

    // get the LockId from the message sent
    token = strtok(NULL, ",");
    if(token) {
        requestedLockId = atoi(token);
    }
    if(requestedCVId != -1 && requestedLockId != -1) {
        printf("Machine %i at box %i wants to Broadcast across CV %i with Lock %i.\n", message->GetClientMachineId(), message->GetClientMailBox(), requestedCVId, requestedLockId);

        if(requestedCVId >=0 && requestedCVId < currentConditionId &&
           requestedLockId >=0 && requestedLockId < currentLockId) {

            ServerLock* lock = locksMap[requestedLockId];
            ServerCondition* condition = conditionsMap[requestedCVId];

            if(!lock->IsDeleted() && !condition->IsDeleted()) {

                MailAddress* address = new MailAddress(message->GetClientMachineId(), message->GetClientMailBox(), message->GetOriginalMachineId(), message->GetOriginalMachineMailBox());
                List* peopleToSignal = condition->Broadcast(address, lock);
                while(!peopleToSignal->IsEmpty()) {
                    MailAddress* addressWoken = (MailAddress*)peopleToSignal->Remove();

                    if(myMachineId == addressWoken->OwnersMachineId() && myMailBox == addressWoken->OwnersMailBox()) {
                        printf("Processed Wait CV Request and now sending a response because I was the original destination.\n");
                        // signal the person who was waiting
                        sprintf(data, "%i,%i,", TRUE, requestedLockId);
                        InternalSend("Wait CV Response", data, addressWoken->MachineId(), addressWoken->MailboxNumber(), outPktHdr, outMailHdr);
                    } else {
                        printf("Processed Wait CV Request and NOT sending a response because I was NOT the original destination.\n");
                    }
                }

                if(myMachineId == message->GetOriginalMachineId() && myMailBox == message->GetOriginalMachineMailBox()) {
                    printf("Processed Broadcast CV Request and now sending a response because I was the original destination.\n");
                    // signal the person who called Broadcast
                    sprintf(data, "%i,%i,%i", TRUE, requestedCVId, requestedLockId);
                    InternalSend("Broadcast CV Response", data, message->GetClientMachineId(), message->GetClientMailBox(), outPktHdr, outMailHdr);
                } else {
                    printf("Processed Broadcast CV Request and NOT sending a response because I was NOT the original destination.\n");
                }

            } else {
                printf("Machine %i at box %i wants to Broadcast on a Lock/CV - but part has been scheduled for deletion/is deleted.\n",message->GetClientMachineId(), message->GetClientMailBox());
            }
        } else {
            printf("Machine %i at box %i wants to Broadcast on a Lock/CV - but an invalid Id was given.\n",message->GetClientMachineId(), message->GetClientMailBox());
        }
    }
}

void InternalDestroyCV(char* token, PacketHeader outPktHdr, PacketHeader inPktHdr, MailHeader outMailHdr, MailHeader inMailHdr, MessageToProcess* message) {
    char data[MaxMailSize];
    int requestedCVId = atoi(token);
    printf("Machine %i at box %i wants to Destroy CV #%i.\n",message->GetClientMachineId(), message->GetClientMailBox(), requestedCVId);

    // validate a few things
    if(requestedCVId >= 0 && requestedCVId < currentConditionId) {
        // was at one point a valid CV
        ServerCondition* condition = conditionsMap[requestedCVId];
        if(!condition->IsDeleted() && !condition->IsScheduledForDeletion()) {
            MailAddress* address = new MailAddress(message->GetClientMachineId(), message->GetClientMailBox(), message->GetOriginalMachineId(), message->GetOriginalMachineMailBox());
            condition->DestroyCV(address);

            if(myMachineId == message->GetOriginalMachineId() && myMailBox == message->GetOriginalMachineMailBox()) {
                printf("Processed Destroy CV Request and now sending a response because I was the original destination.\n");
                sprintf(data, "%i,%i", TRUE, requestedCVId);
                InternalSend("Destroy CV Response", data, message->GetClientMachineId(), message->GetClientMailBox(), outPktHdr, outMailHdr);
            } else {
                printf("Processed Destroy CV Request and NOT sending a response because I was NOT the original destination.\n");
            }

        } else {
            printf("Machine %i at box %i wants to Destroy CV #%i - but it is deleted/scheduled for deletion.\n",message->GetClientMachineId(), message->GetClientMailBox(), requestedCVId);
        }
    } else {
        printf("Machine %i at box %i wants to Destroy CV #%i - but it is an invalid Id.\n",message->GetClientMachineId(), message->GetClientMailBox(), requestedCVId);
    }
}

/**********************************************************************************
 *                               END CV CALLS                                     *
 **********************************************************************************/
/**********************************************************************************/
/**********************************************************************************
 *                                 MV CALLS                                       *
 **********************************************************************************/

void InternalCreateIntMV(char* token, PacketHeader outPktHdr, PacketHeader inPktHdr, MailHeader outMailHdr, MailHeader inMailHdr, MessageToProcess* message) {
    char data[MaxMailSize];
    char* name = token;

    token = strtok(NULL, ",");
    if(token) {
        int newMV = atoi(token);
        printf("Machine %i at box %i wants to Create an Int Array MV (%s) with the size '%i'.\n",message->GetClientMachineId(), message->GetClientMailBox(), name, newMV);
        int idOfNewIntMV = -1;

        for(int i = 0; i < currentIntMVId; i++) {
            ServerIntArrayMV* intArrayMv = intMVMap[i];

            if(strcmp(intArrayMv->GetName(), name) == 0) {
                // found a match
                idOfNewIntMV = i;
                i = currentIntMVId;
                intArrayMv->addUser();
            }
        }

        if(idOfNewIntMV == -1) {
            // create the new StringMV
            ServerIntArrayMV* intArrayMV = new ServerIntArrayMV(name, newMV, message->GetClientMachineId());
            idOfNewIntMV = currentIntMVId;
            currentIntMVId++;
            intMVMap.insert(std::pair<int, ServerIntArrayMV*>(idOfNewIntMV, intArrayMV));
        }

        if(myMachineId == message->GetOriginalMachineId() && myMailBox == message->GetOriginalMachineMailBox()) {
            printf("Processed Create MV Request and now sending a response because I was the original destination.\n");
            // send out a response;
            sprintf(data, "%i,%i", TRUE, idOfNewIntMV);
            InternalSend("Create Int MV Response", data, message->GetClientMachineId(), message->GetClientMailBox(), outPktHdr, outMailHdr);
        } else {
            printf("Processed Create MV Request and NOT sending a response because I was NOT the original destination.\n");
        }
    }
}
void InternalAlterIntMV(char* token, PacketHeader outPktHdr, PacketHeader inPktHdr, MailHeader outMailHdr, MailHeader inMailHdr, MessageToProcess* message) {
    char data[MaxMailSize];
    int mvKey = atoi(token);
    int mvIndex = -1;

    token = strtok(NULL, ",");
    // get the key of the MV
    if(token) {
        mvIndex = atoi(token);
    }

    // get the LockId from the message sent
    token = strtok(NULL, ",");
    if(token) {
        int newValue = atoi(token);
        printf("Machine %i at box %i wants to Alter an Int MV(%i) with the value '%i' at index %i.\n", message->GetClientMachineId(), message->GetClientMailBox(), mvKey, newValue, mvIndex);

        if(mvKey >= 0 && mvKey < currentIntMVId) {
            ServerIntArrayMV* intArrayMv = intMVMap[mvKey];

            if(mvIndex >= -1 && mvIndex < intArrayMv->GetSize()) {
                if(message->GetClientMachineId() == intArrayMv->GetMachineId()) {
                    intArrayMv->SetValue(mvIndex, newValue);

                    if(myMachineId == message->GetOriginalMachineId() && myMailBox == message->GetOriginalMachineMailBox()) {
                        printf("Processed Alter MV Request and now sending a response because I was the original destination.\n");
                        // send out a response;
                        sprintf(data, "%i,%i", TRUE, newValue);
                        InternalSend("Alter Int MV Response", data, message->GetClientMachineId(), message->GetClientMailBox(), outPktHdr, outMailHdr);
                    } else {
                        printf("Processed Alter MV Request and NOT sending a response because I was NOT the original destination.\n");
                    }
                } else {
                    printf("Machine %i at box %i wants to Alter Int MV - But it belongs to Machine %i.\n",message->GetClientMachineId(), message->GetClientMailBox(), intArrayMv->GetMachineId());
                }
            } else {
                printf("Machine %i at box %i wants to Alter Int MV - but it is an invalid Index.\n",message->GetClientMachineId(), message->GetClientMailBox());
            }
        } else {
            printf("Machine %i at box %i wants to Alter Int MV - but it is an invalid Id.\n",message->GetClientMachineId(), message->GetClientMailBox());
        }
    }
}

void InternalRetrieveIntMV(char* token, PacketHeader outPktHdr, PacketHeader inPktHdr, MailHeader outMailHdr, MailHeader inMailHdr, MessageToProcess* message) {
    char data[MaxMailSize];
    int mvKey = atoi(token);
    int mvIndex = -1;

    token = strtok(NULL, ",");
    // get the index of the MV
    if(token) {
        mvIndex = atoi(token);
        printf("Machine %i at box %i wants to Retrieve an Int MV %i index: %i.\n",message->GetClientMachineId(), message->GetClientMailBox(), mvKey, mvIndex);

        if(mvKey >= 0 && mvKey < currentIntMVId) {
            ServerIntArrayMV* intArrayMv = intMVMap[mvKey];

            if(mvIndex >= -1 && mvIndex < intArrayMv->GetSize()) {

                if(message->GetClientMachineId() == intArrayMv->GetMachineId()) {

                    if(myMachineId == message->GetOriginalMachineId() && myMailBox == message->GetOriginalMachineMailBox()) {
                        printf("Processed Retrieve MV Request and now sending a response because I was the original destination.\n");
                        // send out a response;
                        sprintf(data, "%i,%i", TRUE, intArrayMv->GetValue(mvIndex));
                        InternalSend("Retrieve Int MV Response", data, message->GetClientMachineId(), message->GetClientMailBox(), outPktHdr, outMailHdr);
                    } else {
                        printf("Processed Retrieve MV Request and NOT sending a response because I was NOT the original destination.\n");
                    }
                } else {
                    printf("Machine %i at box %i wants to Retrieve Int MV - But it belongs to Machine %i.\n",message->GetClientMachineId(), message->GetClientMailBox(), intArrayMv->GetMachineId());
                }
            } else {
                printf("Machine %i at box %i wants to Retreive Int MV - but it is an invalid Index.\n",message->GetClientMachineId(), message->GetClientMailBox());
            }
        } else {
            printf("Machine %i at box %i wants to Retrieve Int MV - but it is an invalid Id.\n",message->GetClientMachineId(), message->GetClientMailBox());
        }
    }
}

void InternalDestroyIntMV(char* token, PacketHeader outPktHdr, PacketHeader inPktHdr, MailHeader outMailHdr, MailHeader inMailHdr, MessageToProcess* message) {
    char data[MaxMailSize];
    int mvKey = atoi(token);
    printf("Machine %i at box %i wants to Destroy an Int MV %i.\n",message->GetClientMachineId(), message->GetClientMailBox(), mvKey);

    if(mvKey >= 0 && mvKey < currentIntMVId) {
        ServerIntArrayMV* intArrayMv = intMVMap[mvKey];

        int owningMachineId = intArrayMv->GetMachineId();
        if(owningMachineId == message->GetClientMachineId()) {
            // current machine owns the MV so allow a delete call
            intArrayMv->removeUser();

            if(intArrayMv->getActiveUsers() == 0) {
                // remove it from the map
                intMVMap.erase(mvKey);
            }

            if(myMachineId == message->GetOriginalMachineId() && myMailBox == message->GetOriginalMachineMailBox()) {
                printf("Processed Destroy MV Request and now sending a response because I was the original destination.\n");
                // send out a response;
                sprintf(data, "%i,%i", TRUE, mvKey);
                InternalSend("DEstroy Int MV Response", data, message->GetClientMachineId(), message->GetClientMailBox(), outPktHdr, outMailHdr);
            } else {
                printf("Processed Destroy MV Request and NOT sending a response because I was NOT the original destination.\n");
            }

        } else {
            printf("Machine %i at box %i can't delete MV %i it is owned by %i.\n", message->GetClientMachineId(), message->GetClientMailBox(), mvKey, owningMachineId);
        }
    } else {
        printf("Machine %i at box %i wants to Retrieve Int MV - but it is an invalid Id.\n",message->GetClientMachineId(), message->GetClientMailBox());
    }
}

/**********************************************************************************
 *                               END MV CALLS                                     *
 **********************************************************************************/

void HandleMessage(MessageToProcess* message, PacketHeader outPktHdr, PacketHeader inPktHdr, MailHeader outMailHdr, MailHeader inMailHdr, char mailBuffer[MaxMailSize]) {
    char* originalRequest = message->GetOriginalMessage();

    printf("Original Request: '%s'.\n", originalRequest);

    char* token = strtok(originalRequest, ",");
    if(token != NULL) {
        int action = atoi(token);
        switch (action) {
            case SC_CreateLock:
                DEBUG('a', "CreateLock syscall.\n");
                // get the name of the Lock to Create
                token = strtok(NULL, ",");
                if(token) {
                    InternalCreateLock(token, outPktHdr, inPktHdr, outMailHdr, inMailHdr, message);
                }
                break;

            case SC_Acquire:
                DEBUG('a', "Acquire syscall.\n");
                // get the id of the lock they wish to acquire
                token = strtok(NULL, ",");
                if(token) {
                    InternalAcquireLock(token, outPktHdr, inPktHdr, outMailHdr, inMailHdr, message);
                }
                break;

            case SC_Release:
                DEBUG('a', "ReleaseLock syscall.\n");
                // get the id of the lock they wish to release
                token = strtok(NULL, ",");
                if(token) {
                    InternalReleaseLock(token, outPktHdr, inPktHdr, outMailHdr, inMailHdr, message);
                }
                break;

            case SC_DestroyLock:
                DEBUG('a', "DestroyLock syscall.\n");
                // get the name of the Lock to Create
                token = strtok(NULL, ",");
                if(token) {
                    InternalDestroyLock(token, outPktHdr, inPktHdr, outMailHdr, inMailHdr, message);
                }
                break;

            case SC_CreateCV:
                DEBUG('a', "CreateCV syscall.\n");
                // get the name of the CV to create
                token = strtok(NULL, ",");
                if(token) {
                    InternalCreateCV(token, outPktHdr, inPktHdr, outMailHdr, inMailHdr, message);
                }
                break;

            case SC_Wait:
                DEBUG('a', "Wait syscall.\n");
                // get the CVId from the message sent
                token = strtok(NULL, ",");
                if(token) {
                    InternalWaitCV(token, outPktHdr, inPktHdr, outMailHdr, inMailHdr, message);
                }
                break;

            case SC_Signal:
                DEBUG('a', "Signal syscall.\n");
                // get the CVId from the message sent
                token = strtok(NULL, ",");
                if(token) {
                    InternalSignalCV(token, outPktHdr, inPktHdr, outMailHdr, inMailHdr, message);
                }
                break;

            case SC_Broadcast:
                DEBUG('a', "Broadcast syscall.\n");
                // get the CVId from the message sent
                token = strtok(NULL, ",");
                if(token) {
                    InternalBroadcastCV(token, outPktHdr, inPktHdr, outMailHdr, inMailHdr, message);
                }
                break;

            case SC_DestroyCV:
                DEBUG('a', "DestroyCV syscall.\n");
                // get the name of the CV to create
                token = strtok(NULL, ",");
                if(token) {
                    InternalDestroyCV(token, outPktHdr, inPktHdr, outMailHdr, inMailHdr, message);
                }
                break;

            case SC_CreateIntMV:
                DEBUG('a', "CreateIntMV syscall.\n");
                // get the int that the user wants to save
                token = strtok(NULL, ",");
                if(token) {
                    InternalCreateIntMV(token, outPktHdr, inPktHdr, outMailHdr, inMailHdr, message);
                }
                break;

            case SC_AlterIntMV:
                DEBUG('a', "AlterIntMV syscall.\n");
                // get the int that the user wants to save
                token = strtok(NULL, ",");
                // get the key of the MV
                if(token) {
                    InternalAlterIntMV(token, outPktHdr, inPktHdr, outMailHdr, inMailHdr, message);
                }
                break;

            case SC_RetrieveIntMV:
                DEBUG('a', "RetrieveIntMV syscall.\n");
                // get the int that the user wants
                token = strtok(NULL, ",");

                // get the key of the MV
                if(token) {
                    InternalRetrieveIntMV(token, outPktHdr, inPktHdr, outMailHdr, inMailHdr, message);
                }
                break;

            case SC_DestroyIntMV:
                DEBUG('a', "DestroyIntMV syscall.\n");
                // get the int that the user wants
                token = strtok(NULL, ",");

                // get the key of the MV
                if(token) {
                    InternalDestroyIntMV(token, outPktHdr, inPktHdr, outMailHdr, inMailHdr, message);
                }
                break;

            default:
                printf("Unknown Request sent to Handle Message.\n");
                break;
        }
    }
}

void ProcessMessages(PacketHeader outPktHdr, PacketHeader inPktHdr, MailHeader outMailHdr, MailHeader inMailHdr, char mailBuffer[MaxMailSize]) {

    int64_t keyOfNextMessage;

    MessageToProcess* peekAtMessage = (MessageToProcess*)messagesToHandle->SortedPeek(&keyOfNextMessage);

    bool canProcess = true;
    while(canProcess && peekAtMessage != NULL) {
        // check key against timestamp of everyone
        printf("Checking timestamped Item against the current list.\n");
        for(int i = 0; i < NUMBER_OF_SERVERS; i++) {
            printf("Machine Id %i has timestamp %i.\n", i, currentTimeOfOtherServers[i]);
            if(currentTimeOfOtherServers[i] < peekAtMessage->GetTimestamp()) {
                printf("Cannot handle message, Machine %i's timestamp is too small.\n", i);
                canProcess = false;
                i = NUMBER_OF_SERVERS;
            }
        }

        if(canProcess) {
            MessageToProcess* nextPossibleMessage = NULL;
            nextPossibleMessage = (MessageToProcess*)messagesToHandle->SortedRemove(&keyOfNextMessage);
            HandleMessage(nextPossibleMessage, outPktHdr, inPktHdr, outMailHdr, inMailHdr, mailBuffer);
            // get next Message as well
            peekAtMessage = (MessageToProcess*)messagesToHandle->SortedPeek(&keyOfNextMessage);
        } else {
//            printf("Putting Message Back in queue.\n");
//            messagesToHandle->SortedInsert(nextPossibleMessage, keyOfNextMessage);
        }
    }
}

void
StartServer(int boxId) {
    bool running = TRUE;

    PacketHeader outPktHdr, inPktHdr;
    MailHeader outMailHdr, inMailHdr;
    char mailBuffer[MaxMailSize];
    char data[MaxMailSize];
    bool success;

    outMailHdr.from = boxId;        // my mailbox

    int requestedCVId = -1;
    int requestedLockId = -1;

    int mvKey = -1;
    int mvIndex = -1;

    int reqestedMVInt;
    char* requestedMVChar;

    while(running) {
        // Wait for a message from the other 'machine'
        postOffice->Receive(boxId, &inPktHdr, &inMailHdr, mailBuffer);

        myMachineId = inPktHdr.to;

        char* originalMailBuffer = new char[MaxMailSize];
        strcpy(originalMailBuffer, mailBuffer);

        char* token = strtok(mailBuffer, ",");
        if(token != NULL) {
            int action = atoi(token);
            switch (action) {
                case SC_CreateLock:
                    DEBUG('a', "CreateLock syscall.\n");
                    // get the name of the Lock to Create
                    token = strtok(NULL, ",");
                    if(token) {
                        ForwardMessageToServers(inPktHdr, inMailHdr, outPktHdr, outMailHdr, originalMailBuffer);
                    }
                    break;

                case SC_Acquire:
                    DEBUG('a', "Acquire syscall.\n");
                    // get the id of the lock they wish to acquire
                    token = strtok(NULL, ",");
                    if(token) {
                        ForwardMessageToServers(inPktHdr, inMailHdr, outPktHdr, outMailHdr, originalMailBuffer);
                    }
                    break;

                case SC_Release:
                    DEBUG('a', "ReleaseLock syscall.\n");
                    // get the id of the lock they wish to release
                    token = strtok(NULL, ",");
                    if(token) {
                        ForwardMessageToServers(inPktHdr, inMailHdr, outPktHdr, outMailHdr, originalMailBuffer);
                    }
                    break;

                case SC_DestroyLock:
                    DEBUG('a', "DestroyLock syscall.\n");
                    // get the name of the Lock to Create
                    token = strtok(NULL, ",");
                    if(token) {
                        ForwardMessageToServers(inPktHdr, inMailHdr, outPktHdr, outMailHdr, originalMailBuffer);
                    }
                    break;

                case SC_CreateCV:
                    DEBUG('a', "CreateCV syscall.\n");
                    // get the name of the CV to create
                    token = strtok(NULL, ",");
                    if(token) {
                        ForwardMessageToServers(inPktHdr, inMailHdr, outPktHdr, outMailHdr, originalMailBuffer);
                    }
                    break;

                case SC_Wait:
                    DEBUG('a', "Wait syscall.\n");
                    // get the CVId from the message sent
                    token = strtok(NULL, ",");
                    if(token) {
                        ForwardMessageToServers(inPktHdr, inMailHdr, outPktHdr, outMailHdr, originalMailBuffer);
                    }
                    break;

                case SC_Signal:
                    DEBUG('a', "Signal syscall.\n");
                    // get the CVId from the message sent
                    token = strtok(NULL, ",");
                    if(token) {
                        ForwardMessageToServers(inPktHdr, inMailHdr, outPktHdr, outMailHdr, originalMailBuffer);
                    }
                    break;

                case SC_Broadcast:
                    DEBUG('a', "Broadcast syscall.\n");
                    // get the CVId from the message sent
                    token = strtok(NULL, ",");
                    if(token) {
                        ForwardMessageToServers(inPktHdr, inMailHdr, outPktHdr, outMailHdr, originalMailBuffer);
                    }
                    break;

                case SC_DestroyCV:
                    DEBUG('a', "DestroyCV syscall.\n");
                    // get the name of the CV to create
                    token = strtok(NULL, ",");
                    if(token) {
                        ForwardMessageToServers(inPktHdr, inMailHdr, outPktHdr, outMailHdr, originalMailBuffer);
                    }
                    break;

                case SC_CreateIntMV:
                    DEBUG('a', "CreateIntMV syscall.\n");
                    // get the int that the user wants to save
                    token = strtok(NULL, ",");
                    if(token) {
                        ForwardMessageToServers(inPktHdr, inMailHdr, outPktHdr, outMailHdr, originalMailBuffer);
                    }
                    break;

                case SC_AlterIntMV:
                    DEBUG('a', "AlterIntMV syscall.\n");
                    // get the int that the user wants to save
                    token = strtok(NULL, ",");
                    // get the key of the MV
                    if(token) {
                        ForwardMessageToServers(inPktHdr, inMailHdr, outPktHdr, outMailHdr, originalMailBuffer);
                    }
                    break;

                case SC_RetrieveIntMV:
                    DEBUG('a', "RetrieveIntMV syscall.\n");
                    // get the int that the user wants
                    token = strtok(NULL, ",");

                    // get the key of the MV
                    if(token) {
                        ForwardMessageToServers(inPktHdr, inMailHdr, outPktHdr, outMailHdr, originalMailBuffer);
                    }
                    break;

                case SC_DestroyIntMV:
                    DEBUG('a', "DestroyIntMV syscall.\n");
                    // get the int that the user wants
                    token = strtok(NULL, ",");

                    // get the key of the MV
                    if(token) {
                        ForwardMessageToServers(inPktHdr, inMailHdr, outPktHdr, outMailHdr, originalMailBuffer);
                    }
                    break;

                case ServerForwardedMessage: {
                    DEBUG('a', "Server Forwarded Message");

                    int forwardingServerMachine = inPktHdr.from;
                    int forwardingServerMailBox = inMailHdr.from;

                    // get the forwarding servers timestamp
                    token = strtok(NULL, ",");
                    int forwardedTimeStamp = atoi(token);

                    // get the Original messages Machine ID
                    token = strtok(NULL, ",");
                    int clientMachineID = atoi(token);

                    // get the original messages mailbox number
                    token = strtok(NULL, ",");
                    int clientMailboxNumber = atoi(token);

                    // get the original message itself
                    char* originalMessage = new char[MaxMailSize];
                    token = strtok(NULL, ",");
                    while(token) {
                        strcat(originalMessage, token);
                        strcat(originalMessage, ",");
                        token = strtok(NULL, ",");
                    }
                    originalMessage[strlen(originalMessage)-1] = '\0';

                    printf("Was forwarded a message by Server Machine %i - box %i at time %i. It originally came from Client Machine %i - box %i. The Message was '%s'.\n",
                            forwardingServerMachine, forwardingServerMailBox, forwardedTimeStamp, clientMachineID, clientMailboxNumber, originalMessage);

                    printf("Adding message into messageQueue.\n");
                    // TODO MAYBE EASY?
                    //int64_t key = (forwardedTimeStamp * 10) + forwardingServerMachine;
                    int64_t key = forwardedTimeStamp;

                    MessageToProcess* messageToProcess = new MessageToProcess(forwardingServerMachine, forwardingServerMailBox, clientMachineID, clientMailboxNumber, originalMessage, forwardedTimeStamp);

                    messagesToHandle->SortedInsert(messageToProcess, key);

                    printf("Updating last timestamp table with new value\n");
                    currentTimeOfOtherServers[forwardingServerMachine] = forwardedTimeStamp;
                    if(currentTime <= forwardedTimeStamp) {
                        currentTime = forwardedTimeStamp +1;
                        currentTimeOfOtherServers[myMachineId] = currentTime;
                    }

                    printf("Sending Heartbeat to everyone now.\n");
                    SendHeartBeatMessage(currentTime, outPktHdr, outMailHdr);

                    printf("Processing Messages.\n");
                    ProcessMessages(outPktHdr, inPktHdr, outMailHdr, inMailHdr, mailBuffer);

                    delete originalMessage;
                    break;
                }
                case ServerHeartbeat: {
                    DEBUG('a', "Server Forwarded Message");

                    // get the servers timestamp
                    token = strtok(NULL, ",");
                    int heartbeatTimestamp = atoi(token);

                    // get the machine Id
                    int heartbeatMachineId = inPktHdr.from;

                    printf("Got a heartbeat from Machine %i for time %i. Updating table.\n", heartbeatMachineId, heartbeatTimestamp);
                    currentTimeOfOtherServers[heartbeatMachineId] = heartbeatTimestamp;

                    printf("Processing Messages.\n");
                    ProcessMessages(outPktHdr, inPktHdr, outMailHdr, inMailHdr, mailBuffer);

                    break;
                }
                default:
                    printf("Unknown Request sent to Server.\n");
                    break;
            }

            delete originalMailBuffer;
        }

        requestedCVId = -1;
        requestedLockId = -1;

        mvKey = -1;
        mvIndex = -1;
    }
}
